/*
* Copyright (c) 2006-2009 Erin Catto http://www.box2d.org
*
* This software is provided 'as-is', without any express or implied
* warranty.  In no event will the authors be held liable for any damages
* arising from the use of this software.
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.

********************************************************************************

SandBox2D - Tools and Middleware 2011/2012 Assignment 1  - Sam Latif MA002HA

A heavily modified testbed, with added dynamic shape creation/editing, FMOD sounds and XML meta data save/load functionality

*/

#ifndef SANDBOX2D_H
#define SANDBOX2D_H

#include <cstdio>
#include <cstdlib>
#include <vector>

#include <Box2D/Box2D.h>

#include <fmod.hpp>
#include <fmod_errors.h>

#include "tinyxml\tinyxml.h"
#include "tinyxml\GsgMeta.h"
#include "tinyxml\GsgXMLReader.h"
#include "tinyxml\GsgXMLWriter.h"

#include "Render.h"
#include "Box.h"
#include "Ball.h"

class SandBox2D;

/// SandBox2D settings. Some can be controlled in the GUI.
struct Settings
{
	Settings() :
		viewCenter(0.0f, 20.0f),
		hz(60.0f),
		screenWidth(1280),
		screenHeight(900),
		pause(0),
		singleStep(0)
		{}

	b2Vec2 viewCenter;
	float32 hz;
	int32 screenWidth;
	int32 screenHeight;
	
	bool showHelp;

	int bodyType;
	int bodyFixRotation;
	float bodyMass;
	float bodyFriction;
	float bodyRestitution;

	b2Vec2 boxSize;
	
	float boxWidth;
	float boxHeight;

	float ballSize;
	int32 numberOfBoxes;
	int32 numberOfBalls;

	b2Vec2 gravity;

	int32 pause;
	int32 singleStep;
};

// This is called when a joint in the world is implicitly destroyed
// because an attached body is destroyed. This gives us a chance to
// nullify the mouse joint.
class DestructionListener : public b2DestructionListener
{
public:
	void SayGoodbye(b2Fixture* fixture) { B2_NOT_USED(fixture); }
	void SayGoodbye(b2Joint* joint);

	SandBox2D* sandBoxApp;
};

const int32 k_maxContactPoints = 2048;

struct ContactPoint
{
	b2Fixture* fixtureA;
	b2Fixture* fixtureB;
	b2Vec2 normal;
	b2Vec2 position;
	b2PointState state;
};

class SandBox2D : public b2ContactListener
{
public:

	SandBox2D();
	virtual ~SandBox2D();

	void SetTextLine(int32 line) { m_textLine = line; }
	void RenderText(int x, int y, const char *string);
	void Step(Settings* settings);
	void Keyboard(unsigned char key) { B2_NOT_USED(key); }
	void KeyboardUp(unsigned char key) { B2_NOT_USED(key); }
	void ShiftMouseDown(const b2Vec2& p);
	void MouseDown(const b2Vec2& p);
	void MouseUp(const b2Vec2& p);
	void MouseMove(const b2Vec2& p);
	void BeginContact(b2Contact* contact);

	void GenerateBox(int bodyType, int fixedRotation, b2Vec2 size, b2Vec2 position, float mass, float restitution, float friction, int quantity);
	void GenerateBall(int bodyType, int fixedRotation, float radius, b2Vec2 position, float angle, float density, float restitution, float friction, int numOfBalls);
	void SetShape(int bodyType, int fixRotation, b2Vec2 boxSize, float ballSize, float mass, float restitution, float friction);
	void DeleteShape();
	void SetGravity(b2Vec2 gravity);
	void meta(GsgMeta &meta);
	void SaveMeta();
	void LoadMeta();

	Box SelectedBox;
	Ball SelectedCircle;
	b2Body* SelectedShape;

	//vector list containers for our shapes
	std::vector<Box> boxes; 
	std::vector<Ball> balls;

	// Let derived class know that a joint was destroyed.
	void JointDestroyed(b2Joint* joint) { B2_NOT_USED(joint); }

	// Callbacks for derived classes.
	void PreSolve(b2Contact* contact, const b2Manifold* oldManifold);
	void PostSolve(const b2Contact* contact, const b2ContactImpulse* impulse)
	{
		B2_NOT_USED(contact);
		B2_NOT_USED(impulse);
	}


protected:

	enum shapeTypeDef { ballShape = 0, boxShape = 1 };
	int shapeType;
	
	friend class DestructionListener;
	friend class BoundaryListener;
	friend class ContactListener;

	b2Body* m_groundBody;
	b2AABB m_worldAABB;
	ContactPoint m_points[k_maxContactPoints];
	int32 m_pointCount;
	DestructionListener m_destructionListener;
	DebugDraw m_debugDraw;
	int32 m_textLine;
	b2World* m_world;
	b2MouseJoint* m_mouseJoint;
	b2Vec2 m_mouseWorld;
	int32 m_stepCount;

	b2Profile m_maxProfile;
	b2Profile m_totalProfile;
	
	FMOD_RESULT result;
	FMOD::System *fmodSystem;
	FMOD::Sound *sound;
};

#endif
